package com.icw.decoupling.impl.tool;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import com.icw.configuration.data_interchange.RpcResult;
import com.icw.decoupling.defi.expand.TimerJob;
import com.icw.decoupling.defi.proxy.GenericSrvProxy;
import com.icw.decoupling.defi.tool.DataExchange;
import com.icw.decoupling.defi.tool.TimerJobManager;
import com.icw.resource.inner.entity.IcwTimerJob;
import com.icw.utility.BeanCtxUtils;
import com.icw.utility.IcwUtils;
import com.icw.utility.query.IcwCriteria;

@Component
public class QuatzTimerJobManager implements TimerJobManager, InitializingBean, DisposableBean {

	private Scheduler sc;

	private static Map<String, TimerJob> name_job_map = new HashMap<>();

	public QuatzTimerJobManager() throws Exception {
		for (Entry<String, TimerJob> entry : map.entrySet()) {
			name_job_map.put(entry.getKey(), entry.getValue());
		}
	}

	@Override
	public void start(IcwTimerJob timerJob) throws Exception {
		synchronized (timerJob.getName()) {
			JobDetail jobDetail = jobDetail(timerJob);
			Trigger trigger = trigger(timerJob);
			sc.scheduleJob(jobDetail, trigger);
		}
	}

	@Override
	public void stop(IcwTimerJob timerJob) throws Exception {
		sc.deleteJob(jobKey(timerJob));
	}

	private JobKey jobKey(IcwTimerJob timerJob) {
		return new JobKey(timerJob.getName());
	}

	private JobDetail jobDetail(IcwTimerJob timerJob) throws Exception {
		if (sc.getJobDetail(jobKey(timerJob)) != null) {
			sc.deleteJob(jobKey(timerJob));
		}
		JobDetail detail = JobBuilder.newJob(CommonJob.class)// e
				.usingJobData("name", timerJob.getName())// e
				.withIdentity(jobKey(timerJob)).build();
		return detail;
	}

	private Trigger trigger(IcwTimerJob timerJob) throws Exception {
		return TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule(timerJob.getCron())).build();
	}

	public static class CommonJob implements Job {

		@Override
		public void execute(JobExecutionContext context) throws JobExecutionException {
			IcwUtils.tryCatch(() -> {
				Date begin = new Date();
				GenericSrvProxy srv = BeanCtxUtils.getBeanByType(GenericSrvProxy.class);
				DataExchange de = BeanCtxUtils.getBeanByType(DataExchange.class);
				TimerJob tje = (TimerJob) name_job_map.get(context.getJobDetail().getJobDataMap().get("name"));
				IcwCriteria cri = new IcwCriteria("eq", "name", tje.getName());
				srv.updateByCriteria(IcwTimerJob.class, IcwUtils.mapObject("lastBeginTime", begin, "lastEndTime", null, "lastSpendSeconds", null), cri);
				Map<String, Object> dataOut = IcwUtils.mapObject();
				RpcResult lastExecuteResult = IcwUtils.trySuccess(() -> tje.execute(dataOut), () -> new RpcResult(true, null, dataOut), e -> new RpcResult(e));
				Date end = new Date();
				srv.updateByCriteria(IcwTimerJob.class, IcwUtils.mapObject(null, null// e
				, "lastResult", de.serialize(lastExecuteResult)// e
				, "lastEndTime", end // e
				, "lastSuccess", lastExecuteResult.isResult()// e
				, "lastSpendSeconds", (end.getTime() - begin.getTime()) / 1000// e
				), cri);
			});
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		(sc = StdSchedulerFactory.getDefaultScheduler()).start();
	}

	@Override
	public void destroy() throws Exception {
		sc.shutdown();
	}
}
